using System;
using System.Collections.Generic;
using System.Linq;
using Engine;
using Engine.Graphics;

namespace RuthlessConquest {
    public class ShipsModule : Module {
        public HashSet<Ship> publicShips = [];

        //public DynamicArray<Ship> TmpShipList = [];
        public HashSet<Ship> Ships => publicShips;
        public int[] FactionOwnShips = new int[7];

        public ShipsModule(Game game) : base(game) {
            /*game.EntityAdded += delegate(Entity e) {
                if (e is Ship item) {
                    publicShips.Add(item);
                }
            };
            game.EntityRemoved += delegate(Entity e) {
                if (e is Ship item2) {
                    publicShips.Remove(item2);
                }
            };*/
        }

        public void SendShips(Planet source, Planet destination, int count, Faction giftToFaction) {
            SendShips(source, [destination], count, giftToFaction);
        }

        public void SendShips(Planet source, IEnumerable<Planet> route, int count, Faction giftToFaction) {
            count = MathUtils.Min(count, source.ShipsCount);
            if (count <= 0
                || route.Count() <= 0
                || route.First() == source) {
                return;
            }
            Point2 point = route.First().Position - source.Position;
            int length = IntMath.ApproxLength(point);
            Point2 point2 = length > 0 ? point * source.Radius / length / 2 : Point2.Zero;
            for (int i = 0; i < count; i++) {
                Point2 position = source.Position + point2 + new Point2(StepModule.Random.Int(-100, 100), StepModule.Random.Int(-100, 100));
                Game.AddEntity(new Ship(source.Faction, giftToFaction, position, source, route));
            }
            source.ShipsCount -= count;
            if (SettingsManager.DisplayMoreNotices) {
                float h = source.Radius + 100;
                Vector2 position = new(source.Position.X + h * point.X / length, source.Position.Y + h * point.Y / length);
                source.SentShipsNotices.Add(new Planet.SentShipNotice(StepModule.StepIndex, position, count));
            }
            float volume = MathUtils.Lerp(0f, 0.5f, MathUtils.Saturate(0.01f * (count - 7)));
            float pitch = MathUtils.Lerp(1f, 0.5f, MathUtils.Saturate(0.003f * (count - 100)));
            float pan = MathUtils.Clamp(0.8f * (source.Position.X - Game.WorldSize.X / 2) / (Game.WorldSize.X / 2), -1f, 1f);
            if (volume > 0.01f) {
                Time.QueueTimeDelayedExecution(
                    Time.FrameStartTime + 0.2f * (source.GetHashCode() % 1000 / 1000f),
                    delegate { AudioManager.PlaySound(Sounds.Spawn, true, volume, pitch, pan); }
                );
            }
        }

        public void SendOutsideShips(Faction faction, Planet destination, int shipsCount) {
            Rectangle rectangle = new(0, 0, Game.WorldSize.X, Game.WorldSize.Y);
            Point2 point;
            while (true) {
                point = (X: StepModule.Random.Int(-1000000, 1000000), Y: StepModule.Random.Int(-1000000, 1000000));
                if (!rectangle.Contains(point)) {
                    point.X = MathUtils.Clamp(point.X, rectangle.Left, rectangle.Right);
                    point.Y = MathUtils.Clamp(point.Y, rectangle.Top, rectangle.Bottom);
                    if (MathUtils.Abs(point.X - destination.Position.X) >= rectangle.Size.X / 3
                        && MathUtils.Abs(point.Y - destination.Position.Y) >= rectangle.Size.Y / 3
                        && IntMath.ApproxDistance(point, destination.Position) >= IntMath.ApproxLength(rectangle.Width, rectangle.Height) / 3) {
                        break;
                    }
                }
            }
            Point2 point2 = point;
            Point2 point3 = Point2.Zero;
            if (point2.X <= rectangle.Left) {
                point3 = new Point2(-1, 0);
            }
            else if (point2.X >= rectangle.Right) {
                point3 = new Point2(1, 0);
            }
            else if (point2.Y <= rectangle.Top) {
                point3 = new Point2(0, -1);
            }
            else if (point2.Y >= rectangle.Bottom) {
                point3 = new Point2(0, 1);
            }
            Point2 point4 = new(point3.Y, -point3.X);
            Planet[] route = [destination];
            for (int i = 0; i < shipsCount; i++) {
                Point2 position = point2;
                position += point3 * StepModule.Random.Int(5000, 5000 + shipsCount * 30);
                position += point4 * StepModule.Random.Int(-shipsCount * 20, shipsCount * 20);
                Game.AddEntity(new Ship(faction, Faction.None, position, null, route));
            }
        }

        public void Draw(Color colorTransform) {
            foreach (Ship ship in Ships) {
                ship.Draw(colorTransform);
            }
            CameraModule.PrimitivesRenderer.Flush(CameraModule.WorldToScreenMatrix * PrimitivesRenderer2D.ViewportMatrix());
        }

        public int CountFactionShips(Faction faction) =>
            PlanetsModule.FactionOwnPlanets[(int)faction].Sum(p => p.ShipsCount) + FactionOwnShips[(int)faction];

        public bool TestReachability(Point2 p1, Point2 p2) => IntMath.ApproxDistance(p1, p2) <= Game.CreationParameters.ShipRange;

        public void Step() {
            //TmpShipList.Clear();
            //TmpShipList.AddRange(publicShips);
            foreach (Ship ship in publicShips) {
                if (ship.Game == Game) {
                    ship.Step();
                }
            }
        }
    }
}